/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <ws2tcpip.h>
#include <conio.h>
#include <windows.h>
#include <ctime>
#include <cassert>
#include <QMessageBox>
#include <shlwapi.h>
#include <QDir>
#include "iphlpapi.h"
#include "util.h"
#include "BurnToolCommon/Global.h"
#include "../platform/Securec/securec.h"
#include "LibMain.h"

using namespace UtilClass;
using namespace LibMainClass;
namespace {
    const int SOCKET_VERSION_LOW_BYTE = 2;
    const int SOCKET_VERSION_HIGH_BYTE = 2;
    const int SOCKET_ADDR_MIN = 2;
    const int SOCKET_ADDR_MAX = 2;
    const int IP_ADDR_LEN = 16;
}

char LibMain::netcardList[MAX_NETCARD_NUM][MAX_NETCARD_DESCRIPTION_LEN + 1] = {0};
char LibMain::netcardIP[NETCARD_IP_LEN] = {0};
BOOL LibMain::bIsSendingPack = FALSE;
unsigned long LibMain::ulSendImgFileNum = 0;
unsigned long LibMain::ulImgFileLen = 0;
unsigned long LibMain::ulRetCode = MUT_OK; // 线程退出代码
struct in_addr LibMain::stTargetMulticastIP; // 多播IP结构
unsigned long LibMain::ulStopFlag = UPG_THREAD_RUN;
char LibMain::pszImgFileName[MAX_FILE_PATH + 1];
char LibMain::pszIniFileName[MAX_FILE_PATH + 1] = {0};
char LibMain::pszOneSegmentBuff[ONE_SEGMENT_BUFF_LEN + 1] = {0};
char LibMain::pszMulticastIp[NETCARD_IP_LEN] = {0};
char LibMain::pszIntervalTime[INTERVAL_TIME_LEN] = {0};
unsigned int LibMain::ulUpgPolicy = MUPG_POLICY_UNKNOWN;
unsigned long LibMain::ulPacketNo = 0; /* 发包顺序号 */
char LibMain::pszPacketBuff[ONE_PACKET_BUFF_LEN] = {0};
LibMain::PATITION LibMain::stPatitions[PARTITION_MAX] = {0};
char LibMain::pszPkgIDValue[MAX_PKG_ID_LEN + 1] = {0};
char LibMain::pszProductIDValue[MAX_PRODUCT_ID_LEN + 1] = {0};
char LibMain::pszFileOneLineBuff[MAX_FILE_ONE_LINE_LEN + 1] = {0};
char LibMain::filetypeDescribe = 0; /* 包含的文件类型描述位 */
unsigned long LibMain::ulImgFileCRC = 0;
LibMain::GROUP_UPG_PACKAGE_ST LibMain::stMultiHGWHead;

unsigned long LibMain::AddMulticastGroup(long ulTmp, SOCKET &hSocket, unsigned long ulAddr)
{
    long ulRet;
    unsigned long addr = 0;
    struct ip_mreq stMreq;

    stMreq.imr_multiaddr.s_addr = stTargetMulticastIP.s_addr;
    stMreq.imr_interface.s_addr = ulAddr;
    addr = 0x000000FF & stMreq.imr_multiaddr.s_addr ;
    if (SOCKET_ADDR_MIN <= addr && addr <= SOCKET_ADDR_MAX) {
        ulRet = setsockopt(hSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast<char*>(&stMreq), sizeof(stMreq));
        if (ulRet == SOCKET_ERROR) {
            MutWinsockDeInit(hSocket);
            return TSP_D_ERR_MUT_SOCKET_ERR;
        }

        // 设置多播TTL值
        ulTmp = TTL_VALUE;
        ulRet = setsockopt(hSocket, IPPROTO_IP, IP_MULTICAST_TTL, reinterpret_cast<char*>(&ulTmp), sizeof(ulTmp));
        if (ulRet == SOCKET_ERROR) {
            MutWinsockDeInit(hSocket);
            return TSP_D_ERR_MUT_SOCKET_ERR;
        }

        // 去除loopback
        BOOL bTmp = FALSE;
        setsockopt(hSocket, IPPROTO_IP, IP_MULTICAST_LOOP, reinterpret_cast<char*>(&bTmp), sizeof(bTmp));
        ulRet = setsockopt(hSocket, IPPROTO_IP, IP_MULTICAST_IF,
            reinterpret_cast<char*>(&ulAddr), sizeof(ulAddr));
        if (ulRet == SOCKET_ERROR) {
            MutWinsockDeInit(hSocket);
            return TSP_D_ERR_MUT_SOCKET_ERR;
        }
    }
    return MUT_OK;
}

unsigned long LibMain::MutWinsockInit(SOCKET &hSocket, const char* sInterfaceIPAddress)
{
    WSADATA stWSAData;
    WORD wVersionRequested;
    long ulTmp;
    long ulRet;
    struct sockaddr_in stLocal;
    unsigned long ulAddr;

    if (strcmp(sInterfaceIPAddress, INVALID_IP) == 0) {
        return TSP_D_ERR_MUT_NETCARD_IP_ERR;
    }
    ulAddr = inet_addr(sInterfaceIPAddress);
    // 设置socket版本
    wVersionRequested = MAKEWORD(SOCKET_VERSION_LOW_BYTE, SOCKET_VERSION_HIGH_BYTE);
    ulTmp = WSAStartup(wVersionRequested, &stWSAData);
    if (ulTmp != 0) {
        return TSP_D_ERR_MUT_SOCKET_ERR;
    }

    if (LOBYTE(stWSAData.wVersion) != SOCKET_VERSION_LOW_BYTE
        || HIBYTE(stWSAData.wVersion) != SOCKET_VERSION_HIGH_BYTE) {
        WSACleanup();
        return TSP_D_ERR_MUT_SOCKET_ERR;
    }
    // 设置UDP方式
    hSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (hSocket == INVALID_SOCKET) {
        WSACleanup();
        return TSP_D_ERR_MUT_SOCKET_ERR;
    }

    // 避免绑定时EADDRINUSE错误
    ulTmp = TRUE;
    ulRet = setsockopt(hSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&ulTmp), sizeof(ulTmp));
    if (ulRet == SOCKET_ERROR) {
        MutWinsockDeInit(hSocket);
        return TSP_D_ERR_MUT_SOCKET_ERR;
    }

    // 命名socket
    stLocal.sin_family = AF_INET;
    stLocal.sin_addr.s_addr = ulAddr;
    stLocal.sin_port =   htons(MULTICAST_PORT);
    ulRet = bind(hSocket, reinterpret_cast<struct sockaddr*>(&stLocal), sizeof(stLocal));
    if (ulRet == SOCKET_ERROR) {
        MutWinsockDeInit(hSocket);
        return TSP_D_ERR_MUT_SOCKET_ERR;
    }

    // 加入多播组
    ulRet = AddMulticastGroup(ulTmp, hSocket, ulAddr);
    if (ulRet == TSP_D_ERR_MUT_SOCKET_ERR) {
        return TSP_D_ERR_MUT_SOCKET_ERR;
    }
    return MUT_OK;
}

unsigned long LibMain::MutWinsockDeInit(SOCKET stSocket)
{
    shutdown(stSocket, SD_BOTH);
    closesocket(stSocket);
    WSACleanup();
    return MUT_OK;
}

unsigned long LibMain::MutSendPacket(SOCKET arg)
{
    SOCKET server = arg;
    FILE* pFile;
    unsigned long ulPos = 0;
    unsigned long ulIntvTime;
    BOOL bFlag;
    unsigned long ulPacketLen;
    int lErrorCode = 0;
    
    SOCKADDR_IN remote;
    int ret = memset_s(&remote, sizeof(remote), 0, sizeof(remote));
    if (ret != 0) {
        return TSP_D_ERR_MUT_MALLOC_ERR;
    }
    remote.sin_addr.s_addr = inet_addr(pszMulticastIp);
    remote.sin_family = AF_INET;
    remote.sin_port = htons(MULTICAST_PORT);
    QString cleanPath = QDir::cleanPath(pszImgFileName);
    if (cleanPath.isEmpty()) {
        return false;
    }
    pFile = fopen(cleanPath.toUtf8(), "rb"); // 以二进制方式读文件
    if (pFile == nullptr) {
        QMessageBox::warning(nullptr, "error", "Please make sure the file exist and it is readable!");
        MutWinsockDeInit(server);
        return TSP_D_ERR_MUT_OPEN_FILE_ERR;
    }
    
    if (MutGetImgFileCRC(pFile) != MUT_OK) {
        MutWinsockDeInit(server);
        fclose(pFile);
        return TSP_D_ERR_MUT_CRC_CMP_NOK;
    }
    ulSendImgFileNum = 0;
    bIsSendingPack = TRUE;
    
    // 1. 设置定时器
    ulIntvTime = static_cast<unsigned long>
       (strtol(pszIntervalTime, nullptr, DECIMAL_NUM));
    // 死循环,每一次循环,发送一遍升级包
    unsigned int whileCount = 0;
    while (TRUE == bIsSendingPack) {
        if (whileCount == 10000) { // 循环10000次
            whileCount = 0;
            break;
        }
        whileCount++;
        ulPos = 0;
        if (ulUpgPolicy == MUPG_POLICY_CPE || ulUpgPolicy == MUPG_POLICY_HGW ||
            ulUpgPolicy == MUPG_POLICY_MINISYS || ulUpgPolicy == MUPG_POLICY_MODEM) {
            ulImgFileLen = stMultiHGWHead.ulImageSize;
            ulPacketNo = 0;
            bFlag = TRUE;
            stMultiHGWHead.ucPolicy = ulUpgPolicy;
            // 这个循环负责发送网关包
            while (TRUE == bFlag && TRUE == bIsSendingPack) {
                if (ulPacketNo == 0) {
                    stMultiHGWHead.ulSerialNumber = ulPacketNo;
                    stMultiHGWHead.ulPackageSize = ONE_SEGMENT_BUFF_LEN;
                    MutBuildPartitionPacket(ONE_SEGMENT_BUFF_LEN);
                    // 发送用户输入的数据到多播组
                    ulPacketLen = static_cast<unsigned long>(sendto(server, pszPacketBuff, ONE_PACKET_BUFF_LEN, 0,
                        reinterpret_cast<SOCKADDR*>(&remote), sizeof(remote)));
                    if (ulPacketLen != ONE_PACKET_BUFF_LEN) {
                        lErrorCode = WSAGetLastError();
                        MutWinsockDeInit(server);
                        fclose(pFile);
                        bIsSendingPack = FALSE;
                        // 若出现地址不可达和block错误，线程均不退出，继续尝试发包
                        if (WSAEADDRNOTAVAIL == lErrorCode || WSAEINTR == lErrorCode) {
                            return UPG_D_ERR_MUT_NETWORK_ERR;
                        } else {
                            return UPG_D_ERR_MUT_NETWORK_ERR;
                        }
                    }
                    ulPacketNo++;
                    MutMsTimer(ulIntvTime); // 发包间隔
                }
                // 根据文件剩余长度判断是否为最后一个组播报文
                if ((ulImgFileLen - ulPacketNo * ONE_SEGMENT_BUFF_LEN) > ONE_SEGMENT_BUFF_LEN) {
                    // 获取净荷数据片
                    MutGetFileSegment(pFile, ulPos, pszOneSegmentBuff, ONE_SEGMENT_BUFF_LEN);
                    // 填充组播包头中的其他字段
                    stMultiHGWHead.ulSerialNumber = ulPacketNo;
                    stMultiHGWHead.ulPackageSize = ONE_SEGMENT_BUFF_LEN;
                    MutBuildPacket(pszOneSegmentBuff, ONE_SEGMENT_BUFF_LEN);
                    // 发送用户输入的数据到多播组
                    ulPacketLen = static_cast<unsigned long>(sendto(server, pszPacketBuff, ONE_PACKET_BUFF_LEN, 0,
                        reinterpret_cast<SOCKADDR*>(&remote), sizeof(remote)));
                    if (ulPacketLen != ONE_PACKET_BUFF_LEN) {
                        lErrorCode = WSAGetLastError();
                        MutWinsockDeInit(server);
                        fclose(pFile);
                        bIsSendingPack = FALSE;
                        // 若出现地址不可达和block错误，线程均不退出，继续尝试发包
                        if (WSAEADDRNOTAVAIL == lErrorCode || WSAEINTR == lErrorCode) {
                            return UPG_D_ERR_MUT_NETWORK_ERR;
                        } else {
                            return UPG_D_ERR_MUT_NETWORK_ERR;
                        }
                    }
                    ulPos += ONE_SEGMENT_BUFF_LEN;
                } else {
                    // 发送最后一个包
                    MutGetFileSegment(pFile, ulPos, pszOneSegmentBuff,
                        (ulImgFileLen - ulPacketNo * ONE_SEGMENT_BUFF_LEN));
                    // 填充组播包头中的其他字段
                    stMultiHGWHead.ulSerialNumber = ulPacketNo;
                    stMultiHGWHead.ulPackageSize = ulImgFileLen - ulPacketNo * ONE_SEGMENT_BUFF_LEN;
                    MutBuildPacket(pszOneSegmentBuff,
                        (ulImgFileLen - ulPacketNo * ONE_SEGMENT_BUFF_LEN));
                    ulPacketLen = static_cast<unsigned long>(sendto(server, pszPacketBuff, ONE_PACKET_BUFF_LEN, 0,
                        reinterpret_cast<SOCKADDR*>(&remote), sizeof(remote)));
                    if (ulPacketLen != ONE_PACKET_BUFF_LEN) {
                        lErrorCode = WSAGetLastError();
                        MutWinsockDeInit(server);
                        fclose(pFile);
                        bIsSendingPack = FALSE;
                        // 若出现地址不可达和block错误，线程均不退出，继续尝试发包
                        if (WSAEADDRNOTAVAIL == lErrorCode || WSAEINTR == lErrorCode) {
                            return UPG_D_ERR_MUT_NETWORK_ERR;
                        } else {
                            return UPG_D_ERR_MUT_NETWORK_ERR;
                        }
                    }
                    bFlag = FALSE;
                    ulSendImgFileNum++;
                    
                    // 上限处理
                    if (ulSendImgFileNum == -1L) {
                        ulSendImgFileNum = 0;
                    }

                    ulPacketNo++;

                    // 上限处理
                    if (ulPacketNo == -1L) {
                        ulPacketNo = 0;
                    }
                    MutMsTimer(ulIntvTime); // 发包间隔

                    stMultiHGWHead.ulSerialNumber = ulPacketNo;
                    stMultiHGWHead.ulPackageSize = ONE_SEGMENT_BUFF_LEN;
                    MutBuildEndPacket(ONE_SEGMENT_BUFF_LEN);
                    // 发送用户输入的数据到多播组
                    ulPacketLen = static_cast<unsigned long>(sendto(server, pszPacketBuff, ONE_PACKET_BUFF_LEN, 0,
                        reinterpret_cast<SOCKADDR*>(&remote), sizeof(remote)));
                    if (ulPacketLen != ONE_PACKET_BUFF_LEN) {
                        lErrorCode = WSAGetLastError();
                        MutWinsockDeInit(server);
                        fclose(pFile);
                        bIsSendingPack = FALSE;
                        /* 若出现地址不可达和block错误，线程均不退出，继续尝试发包 */
                        if (WSAEADDRNOTAVAIL == lErrorCode || WSAEINTR == lErrorCode) {
                            return UPG_D_ERR_MUT_NETWORK_ERR;
                        } else {
                            return UPG_D_ERR_MUT_NETWORK_ERR;
                        }
                    }
                    ulPacketNo++;
                    MutMsTimer(ulIntvTime); /* 发包间隔 */
                    break;
                }
                ulPacketNo++;
                
                // 上限处理
                if (ulPacketNo == -1L) {
                    ulPacketNo = 0;
                }
                MutMsTimer(ulIntvTime); // 发包间隔
            }
        }
        MutMsTimer(ulIntvTime); // 发包间隔
    }
    MutWinsockDeInit(server);
    
    fclose(pFile);
    return MUT_OK;
}

unsigned long LibMain::CopyNetCard(PIP_ADAPTER_INFO &pTmpAdapterInfo)
{
    unsigned long ulNetcardNo = 0;
    while (pTmpAdapterInfo && ulNetcardNo < MAX_NETCARD_NUM) {
        int ret = strncpy_s(netcardList[ulNetcardNo], MAX_NETCARD_DESCRIPTION_LEN + 1,
                            pTmpAdapterInfo->Description, NETCARD_DESCRIPTION_INFO);
        if (ret != 0) {
            return TSP_D_ERR_MUT_MALLOC_ERR;
        }
        ret = strcat_s(netcardList[ulNetcardNo], MAX_NETCARD_DESCRIPTION_LEN + 1, "...IP:");
        if (ret != 0) {
            return TSP_D_ERR_MUT_MALLOC_ERR;
        }
        ret = strncat_s(netcardList[ulNetcardNo], MAX_NETCARD_DESCRIPTION_LEN + 1,
            pTmpAdapterInfo->IpAddressList.IpAddress.String, IP_ADDR_LEN);
        if (ret != 0) {
            return TSP_D_ERR_MUT_MALLOC_ERR;
        }
        ulNetcardNo++;
        pTmpAdapterInfo = pTmpAdapterInfo->Next;
    }
    return MUT_OK;
}

unsigned long LibMain::MutGetAllNetworkCard()
{
    DWORD err;
    PIP_ADAPTER_INFO pAdapterInfo = nullptr;
    PIP_ADAPTER_INFO pTmpAdapterInfo = nullptr;
    DWORD adapterInfoSize;
    unsigned long ulI;
    // Enumerate all of the adapter specific information using the IP_ADAPTER_INFO structure.
    // Note:  IP_ADAPTER_INFO contains a linked list of adapter entries.
    adapterInfoSize = 0;
    
    // 初始化网卡信息
    for (ulI = 0; ulI < MAX_NETCARD_NUM; ulI++) {
        int ret = memset_s(netcardList[ulI], MAX_NETCARD_DESCRIPTION_LEN + 1, 0, MAX_NETCARD_DESCRIPTION_LEN + 1);
        if (ret != 0) {
            return TSP_D_ERR_MUT_MALLOC_ERR;
        }
    }
    
    if ((err = GetAdaptersInfo(nullptr, &adapterInfoSize)) != 0) {
        if (err != ERROR_BUFFER_OVERFLOW) {
            return TSP_D_ERR_MUT_GET_ADAPTER_INFO_ERR;
        }
    }
    
    // Allocate memory from sizing information
    if ((pAdapterInfo = (PIP_ADAPTER_INFO) malloc(adapterInfoSize)) == nullptr) {
        return TSP_D_ERR_MUT_GET_ADAPTER_INFO_ERR;
    }
    
    // Get actual adapter information
    
    if ((err = GetAdaptersInfo(pAdapterInfo, &adapterInfoSize)) != 0) {
        free(pAdapterInfo);
        return TSP_D_ERR_MUT_GET_ADAPTER_INFO_ERR;
    }
    
    pTmpAdapterInfo = pAdapterInfo;
    
    err = CopyNetCard(pTmpAdapterInfo);
    if (err != MUT_OK) {
        return TSP_D_ERR_MUT_MALLOC_ERR;
    }
    if (pAdapterInfo != nullptr) {
        free(pAdapterInfo);
        pAdapterInfo = nullptr;
    }
    
    return MUT_OK;
}

unsigned long LibMain::MutGetSelNetCardIP(unsigned long ulNetworkCardNo)
{
    DWORD err;
    PIP_ADAPTER_INFO pAdapterInfo = nullptr;
    PIP_ADAPTER_INFO pTmpAdapterInfo = nullptr;
    DWORD adapterInfoSize;
    unsigned long ulNetcardNo;
    
    //
    // Enumerate all of the adapter specific information using the IP_ADAPTER_INFO structure.
    // Note:  IP_ADAPTER_INFO contains a linked list of adapter entries.
    //
    adapterInfoSize = 0;
    
    if ((err = GetAdaptersInfo(nullptr, &adapterInfoSize)) != 0) {
        if (err != ERROR_BUFFER_OVERFLOW) {
            return TSP_D_ERR_MUT_GET_ADAPTER_INFO_ERR;
        }
    }
    
    // Allocate memory from sizing information
    if ((pAdapterInfo = (PIP_ADAPTER_INFO) malloc(adapterInfoSize)) == nullptr) {
        return TSP_D_ERR_MUT_GET_ADAPTER_INFO_ERR;
    }
    
    // Get actual adapter information
    if ((err = GetAdaptersInfo(pAdapterInfo, &adapterInfoSize)) != 0) {
        free(pAdapterInfo);
        return TSP_D_ERR_MUT_GET_ADAPTER_INFO_ERR;
    }
    
    ulNetcardNo = 0;
    pTmpAdapterInfo = pAdapterInfo;
    
    while (pTmpAdapterInfo && ulNetcardNo < MAX_NETCARD_NUM) {
        if (ulNetcardNo == ulNetworkCardNo) {
            int ret = memcpy_s(netcardIP, NETCARD_IP_LEN,
                pTmpAdapterInfo->IpAddressList.IpAddress.String, NETCARD_IP_LEN);
            if (ret != 0) {
                return TSP_D_ERR_MUT_MALLOC_ERR;
            }
        }
        ulNetcardNo++;
        pTmpAdapterInfo = pTmpAdapterInfo->Next;
    }
    
    if (pAdapterInfo != nullptr) {
        free(pAdapterInfo);
        pAdapterInfo = nullptr;
    }
    return MUT_OK;
}

DWORD LibMain::TspMutMulticast(LPVOID pNetCardNo)
{
    unsigned long ulRet = 0;
    SOCKET stSocket = 0;
    // 网卡down掉后不提示出错，继续循环发包，下次网卡再次up后，重新初始化再发包
    unsigned int whileCount = 0;
    while (1) {
        if (whileCount == 10000) { // 10000 number of cycle
            break;
        }
        whileCount++;
        // 频繁按stop键后，会使socket初始化判断出错，现在一按stop键就停止线程运行
        if (ulStopFlag == UPG_THREAD_STOP) {
            return ulRet;
        }
        stSocket = 0;
        // 重置网卡IP地址
        int ret = memset_s(netcardIP, NETCARD_IP_LEN, 0, NETCARD_IP_LEN);
        if (ret != 0) {
            return TSP_D_ERR_MUT_MALLOC_ERR;
        }
        // 设置多播地址
        MutGetSelNetCardIP(reinterpret_cast<unsigned long long>(pNetCardNo));
        stTargetMulticastIP.s_addr = inet_addr(pszMulticastIp);

        ulRet = MutWinsockInit(stSocket, netcardIP);
        if (ulRet == MUT_OK) {
            ulRet = MutSendPacket(stSocket);
            ulRetCode = ulRet;
            if (ulRet != UPG_D_ERR_MUT_NETWORK_ERR) {
                return ulRet;
            }
        }
    }
    return ulRet;
}
